//
//  HowToUseMDViewController.m
//  MooreDoll
//
//  Created by 郭景豪 on 2014/12/5.
//  Copyright (c) 2014年 Tank. All rights reserved.
//

#import "HowToUseMDViewController.h"
#import "StartAddMDForOnePageCell.h"
#import "StartAddMDForTwoPageCell.h"
#import "StartAddMDFor3PageCell.h"
#import "StartAddMDFor4PageCell.h"
#import "StartAddMDFor5PageCell.h"
#import "StartAddMDFor6PageCell.h"

#import "TelephonyInfo.h"
#import "WriteToFileImage.h"
#import "CoverKeyboard.h"
//UDP {
#import <sys/socket.h>
#import <netinet/in.h>
#import <netinet6/in6.h>
#import <arpa/inet.h>
#import <ifaddrs.h>
#include <netdb.h>
#import <SystemConfiguration/SCNetworkReachability.h>
#import <CFNetwork/CFNetwork.h>
static const NSInteger SERVER_UDP_PORT = 11000;
//UDP }

#import "libxclient.h"
HowToUseMDViewController *howToUseMDVC;
@interface HowToUseMDViewController ()<GetNewToyDelegate,startAddMDFor6PageCellDelegate>
{
    
}
@property (weak, nonatomic) UITextField *TF_ActiveField;

@end

@implementation HowToUseMDViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    [self.teachCollectionView registerNib:[UINib nibWithNibName:@"StartAddMDForOnePageCell" bundle:nil]forCellWithReuseIdentifier:@"StartAddMDForOnePageCell"];//註冊Cell
    [self.teachCollectionView registerNib:[UINib nibWithNibName:@"StartAddMDForTwoPageCell" bundle:nil]forCellWithReuseIdentifier:@"StartAddMDForTwoPageCell"];//註冊Cell
    [self.teachCollectionView registerNib:[UINib nibWithNibName:@"StartAddMDFor3PageCell" bundle:nil]forCellWithReuseIdentifier:@"StartAddMDFor3PageCell"];//註冊Cell
    [self.teachCollectionView registerNib:[UINib nibWithNibName:@"StartAddMDFor4PageCell" bundle:nil]forCellWithReuseIdentifier:@"StartAddMDFor4PageCell"];//註冊Cell
    [self.teachCollectionView registerNib:[UINib nibWithNibName:@"StartAddMDFor5PageCell" bundle:nil]forCellWithReuseIdentifier:@"StartAddMDFor5PageCell"];//註冊Cell
    [self.teachCollectionView registerNib:[UINib nibWithNibName:@"StartAddMDFor6PageCell" bundle:nil]forCellWithReuseIdentifier:@"StartAddMDFor6PageCell"];//註冊Cell

    howToUseMDVC = self;
    [self leftBarButton];
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillShow:)
                                                 name:UIKeyboardDidShowNotification
                                               object:nil];
    
    [[NSNotificationCenter defaultCenter] addObserver:self
                                             selector:@selector(keyboardWillBeHidden:)
                                                 name:UIKeyboardWillHideNotification
                                               object:nil];

}

#pragma mark 文字 鍵盤 delegate
- (void) keyboardWillShow:(NSNotification *)notification
{
    [UIView animateWithDuration:0.3f animations:^
     {
         self.view.frame = CGRectMake(0, -100, self.view.frame.size.width, self.view.frame.size.height);
     }
    ];
}

- (void) keyboardWillBeHidden:(NSNotification *)notification
{
    [UIView animateWithDuration:0.3f animations:^
     {
         self.view.frame = CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height);
     }
     ];
}



-(void)leftBarButton
{
    //顯示navigationBar
    self.navigationController.navigationBarHidden = NO;
    
    //創建button
    UIButton *leftButton = [UIButton buttonWithType:UIButtonTypeCustom];
    
    [leftButton setImage:[UIImage imageNamed:@"top_back"] forState:UIControlStateNormal];
    [leftButton setFrame:CGRectMake(0, 0, [UIImage imageNamed:@"top_back"].size.width, [UIImage imageNamed:@"top_back"].size.height)];
    //button 返回事件
    [leftButton addTarget:self action:@selector(goBackToSourceVC)forControlEvents:UIControlEventTouchUpInside];
    
    UIBarButtonItem *leftBar = [[UIBarButtonItem alloc]initWithCustomView:leftButton];
    
    self.navigationItem.leftBarButtonItem = leftBar;
}
-(void)goBackToSourceVC
{
    if (self.cancelAlertView == nil) {
        self.cancelAlertView = [[UIAlertView alloc]initWithTitle:@"終止啟用" message:@"尚未完成啟用，您是否要終止進行中的流程" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"終止啟用", nil];
    }
    [self.cancelAlertView show];
}
- (IBAction)nextButtonEvent:(id)sender {
    
    //檢查有沒有確實拿到 tuid
    if (self.tuid !=nil) {
        //查看目前是的cell 是哪一個 indexPath
        NSIndexPath *abc =  [[self.teachCollectionView indexPathsForVisibleItems]lastObject];
        
        //檢查當前的 wifi 名稱
        NSString *wifiSSID = [TelephonyInfo currentWifiName];
        
        //如果當前是第0個
        if (abc.item == 0) {
            
            NSIndexPath *indexPath = [NSIndexPath indexPathForItem:1 inSection:0];
            //滑動到第1個 item
            [self.teachCollectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
            
            //START BUTTON HIDDEN
            [self.startButton setHidden:YES];
            [self.cancelButton setHidden:NO];
            [self.nextButton setHidden:NO];
            
            
            //如果當前是第1個
        }else if (abc.item == 1)
        {
            
            //檢查當前的wifi 是不是為 娃娃的apmd
            if ([wifiSSID hasPrefix:@"MDAP"]) {
                //滑向第2個 item
                NSIndexPath *indexPath = [NSIndexPath indexPathForItem:2 inSection:0];
                [self.teachCollectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
                //檢查先前有沒有紀錄到 上一次可以用的wifi名稱 有就幫他帶入到欄位上
                
                //儲存娃娃Wifi 名稱
                [[NSUserDefaults standardUserDefaults]setValue:wifiSSID forKey:@"toyWifi"];
                
            }else
            {
                [[[UIAlertView alloc]initWithTitle:@"" message:@"請確認您的Wifi 已設定為娃娃的Wifi \n例：MDAP XXXX" delegate:nil cancelButtonTitle:@"確認" otherButtonTitles: nil]show];
            }
            //如果當前是第2個
        }else if (abc.item == 2)
        {
            NSIndexPath *indexPath = [NSIndexPath indexPathForItem:2 inSection:0];

            StartAddMDFor3PageCell *cell = (StartAddMDFor3PageCell *)[self.teachCollectionView cellForItemAtIndexPath:indexPath];
            //檢查 使用者有沒有輸入 wifi ssid
            if ([cell.TF_WifiSSID.text isEqualToString:@""]) {
                [[[UIAlertView alloc]initWithTitle:@"" message:@"請輸入您的可用的Wifi 名稱" delegate:nil cancelButtonTitle:@"確認" otherButtonTitles: nil]show];
                //檢查使用者有沒有設定成娃娃wifi
            }else if([wifiSSID hasPrefix:@"MDAP"])
            {
                
                //這邊監控網路狀態
                [[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
                    switch (status) {
                        case AFNetworkReachabilityStatusReachableViaWiFi:
                            
                            if (![[TelephonyInfo currentWifiName]hasPrefix:@"MDAP"]) {
                                NSLog(@"回到網路狀態");
                                
                                //重新連線
                                double delayInSeconds = 2.0;
                                dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
                                dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
                                    if (delayInSeconds != 0.0) {
                                        [[XMPPSharedInstance sharedInstance] login];
                                    }
                                });
                            }
                            break;
                        case AFNetworkReachabilityStatusNotReachable:
                            NSLog(@"無效網路");
//                            [[[UIAlertView alloc]initWithTitle:@"無網路" message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:nil]show];
                        default:
                            break;
                    }
                }];
            
                NSString *userJID = [[[NSUserDefaults standardUserDefaults]objectForKey:@"userJID"]stringByReplacingOccurrencesOfString:@"@bbserver-virtualbox" withString:@""];
                
                NSString *parameter = [NSString stringWithFormat:@"registertoy#%@:!:!#%@:!:!#3:!:!#xmpp.mooredoll.com#%@#%@",cell.TF_WifiSSID.text,cell.TF_WifiPassword.text,userJID,self.tuid];
                

                NSLog(@"%@",parameter);
                if (self.registToyLoading==nil) {
                    
                    self.registToyLoading = [[UIAlertView alloc]initWithTitle:@"注意" message:@"娃娃已收到訊息請耐心等待連線，並請勿關閉娃娃" delegate:nil cancelButtonTitle:nil otherButtonTitles:nil];
                }
                
                //註冊檢查有沒有收到新的娃娃Delegate
                [DBManageController sharedInstance].getNewToyDelegate = self;

                //發送參數給 娃娃
                [self sendUDPSocket:parameter ipAddress:[self getLocalLanAddress:[self getIPAddress]] port:SERVER_UDP_PORT];
                

            }else
            {
                [[[UIAlertView alloc]initWithTitle:@"" message:@"請確認您的Wifi 已設定為娃娃的Wifi \n例：MDAP XXXX" delegate:nil cancelButtonTitle:@"確認" otherButtonTitles: nil]show];
            }
            //當在第三頁的時候 就是失敗頁面
        }else if (abc.item == 3)
        {
            //跳回第靈個頁面重新開始
            NSIndexPath *indexPath = [NSIndexPath indexPathForItem:0 inSection:0];
            [self.teachCollectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
            
            //改變nextButton title
            [self.nextButton setTitle:@"下一步" forState:UIControlStateNormal];
            
            //START BUTTON HIDDEN
            [self.startButton setHidden:NO];
            [self.cancelButton setHidden:YES];
            [self.nextButton setHidden:YES];
            
            //當在第四頁的時候 就是成功頁面
        }else if (abc.item == 4)
        {
            //完成頁面 跳到設定魔豆頁面
            NSIndexPath *indexPath = [NSIndexPath indexPathForItem:5 inSection:0];
            [self.teachCollectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];

        }else if (abc.item == 5)
        {
            //發送 娃娃設定
            
            StartAddMDFor6PageCell *cell = (StartAddMDFor6PageCell *)[self.teachCollectionView cellForItemAtIndexPath:abc];
            
            if (self.toyJid != nil) {

                
                NSString *userImagePath;
                if (cell.photoButton.imageView.image != nil) {
                    //儲存使用者的圖片
                    userImagePath = [[WriteToFileImage sharedInstance]GetAndWriteToFileImage:cell.photoButton.imageView.image fileName:@"toy"];
                }else
                {
                    //儲存使用者的圖片
                    userImagePath = [[WriteToFileImage sharedInstance]GetAndWriteToFileImage:[UIImage imageNamed:@"dficon_modo"] fileName:@"toy"];
                }
                
                NSDictionary *toyDict = @{@"toyJid":self.toyJid,@"toyNickName":cell.TF_Nickname.text,@"toyImage":userImagePath};

                [[XMPPSharedInstance sharedInstance]changeToyVcard:toyDict];
            }
            
        }
     }
    
}

-(void)timerOut
{
    NSLog(@"timer out ");
    //如果正在執行 timer 就將他解除
    [self invalidateTimer];
    //結束 loading 畫面
    [self.registToyLoading dismissWithClickedButtonIndex:0 animated:YES];
    NSIndexPath *indexPath = [NSIndexPath indexPathForItem:3 inSection:0];
    [self.teachCollectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
    //斷開delegate 以免已經時間到了 然後又收到成功的訊息
    [DBManageController sharedInstance].getNewToyDelegate = nil;

    //改變nextButton title
    [self.nextButton setTitle:@"再試一次" forState:UIControlStateNormal];
}
-(void)invalidateTimer
{
    //如果正在執行 timer 就將他解除
    if ([self.registTimerOut isValid]) {
        [self.registTimerOut invalidate];
        self.registTimerOut = nil;
    }
}
-(void)getNewToyResult:(NSString *)toyResult
{
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"娃娃新增成功");
        NSLog(@"%@",toyResult);
        if (toyResult) {
            self.toyJid = toyResult;
        }
        
        [self.registToyLoading dismissWithClickedButtonIndex:-1 animated:NO];
        
        if ([[self.navigationController.viewControllers lastObject]isKindOfClass:[self class]]) {
            //成功跳到第四頁
            NSIndexPath *abc =  [[self.teachCollectionView indexPathsForVisibleItems]lastObject];
            //檢查 不是在
            if (abc.item !=5) {
                NSLog(@"成功");
                NSIndexPath *indexPath = [NSIndexPath indexPathForItem:4 inSection:0];
                [self.teachCollectionView scrollToItemAtIndexPath:indexPath atScrollPosition:UICollectionViewScrollPositionCenteredHorizontally animated:YES];
            }
        }
        [self invalidateTimer];
    });
}

-(void)setToyVcard
{
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.navigationController popToRootViewControllerAnimated:YES];
    });
}

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if (self.cancelAlertView == alertView) {
        if (buttonIndex == 1) {
            [self.navigationController popViewControllerAnimated:YES];
        }
    }
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (NSInteger) collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section
{
    return 6;
    
}

- (UICollectionViewCell *) collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{

    //    [cell.photoImageView layoutIfNeeded];
    switch (indexPath.row) {
        case 0:
            return [self onePage:collectionView cellForItemAtIndexPath:indexPath];
            break;
        case 1:
            return [self twoPage:collectionView cellForItemAtIndexPath:indexPath];
            break;
        case 2:
            
            return [self threePage:collectionView cellForItemAtIndexPath:indexPath];
            break;
        case 3:
            
            return [self fourPage:collectionView cellForItemAtIndexPath:indexPath];
            break;
        case 4:
            
            return [self fivePage:collectionView cellForItemAtIndexPath:indexPath];
            break;
        case 5:
            
            return [self sixPage:collectionView cellForItemAtIndexPath:indexPath];
            break;
        default:
            return nil;
            break;
    }
    
}
-(StartAddMDForOnePageCell*)onePage:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    StartAddMDForOnePageCell *cell = (StartAddMDForOnePageCell*)[collectionView dequeueReusableCellWithReuseIdentifier:@"StartAddMDForOnePageCell" forIndexPath:indexPath];

    return cell;
}
-(StartAddMDForTwoPageCell *)twoPage:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    StartAddMDForTwoPageCell *cell = (StartAddMDForTwoPageCell*)[collectionView dequeueReusableCellWithReuseIdentifier:@"StartAddMDForTwoPageCell" forIndexPath:indexPath];
    
    return cell;
}
-(StartAddMDFor3PageCell *)threePage:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    
    StartAddMDFor3PageCell *cell = (StartAddMDFor3PageCell*)[collectionView dequeueReusableCellWithReuseIdentifier:@"StartAddMDFor3PageCell" forIndexPath:indexPath];
    
    return cell;

}
-(StartAddMDFor4PageCell *)fourPage:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    
    StartAddMDFor4PageCell *cell = (StartAddMDFor4PageCell*)[collectionView dequeueReusableCellWithReuseIdentifier:@"StartAddMDFor4PageCell" forIndexPath:indexPath];
    
    return cell;
    
}

-(StartAddMDFor5PageCell *)fivePage:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    
    StartAddMDFor5PageCell *cell = (StartAddMDFor5PageCell*)[collectionView dequeueReusableCellWithReuseIdentifier:@"StartAddMDFor5PageCell" forIndexPath:indexPath];
    
    return cell;
    
}

-(StartAddMDFor6PageCell *)sixPage:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
    
    StartAddMDFor6PageCell *cell = (StartAddMDFor6PageCell*)[collectionView dequeueReusableCellWithReuseIdentifier:@"StartAddMDFor6PageCell" forIndexPath:indexPath];
    
    if (cell.delegate!=self) {
        cell.delegate = self;
    }
    return cell;
    
}

#pragma mark 大頭貼 delegate
-(void)cellTakePhotoButtonOpenCamera:(UIImagePickerController *)camera
{
    [self presentViewController:camera animated:YES completion:nil];
}
-(void)cellTakePhotoButtonOpenPhotoLibrary:(UIImagePickerController *)PhotoLibrary
{
    [self presentViewController:PhotoLibrary animated:YES completion:nil];
    
}
-(void)cellTakePhotoAlertDismissVC
{
    [self dismissViewControllerAnimated:YES completion:nil];
    
}

-(void)celltakePhotoClance
{
    [self dismissViewControllerAnimated:YES completion:nil];
}

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath;
{
    
        [self.view endEditing:YES];
    
}
#pragma  mark UICollectionViewDelegateFlowLayout
- (CGFloat) collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumLineSpacingForSectionAtIndex:(NSInteger)section
{
    return 0;//item 左右的間隔留10
}

- (CGFloat) collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section
{
    return 0;//上下留0
}

- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath
{
    return CGSizeMake(collectionView.frame.size.width, collectionView.frame.size.height);//item 大小
}

- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout*)collectionViewLayout insetForSectionAtIndex:(NSInteger)section
{
    return UIEdgeInsetsMake(0, 0, 0, 0);//左右邊框留20
}





//{
//UDP {
void handledFromUDPResponse(long int retVal, char *sent_msg, char *received_msg)
{
    printf("\n===handledFromUDPResponse=== retVal(%d)", retVal);
    if (retVal<0) {
        //TODO:ERROR HANDLING
        if (retVal==-1) {
            printf("\n- ERROR TIMEOUT");//逾時
            
            [[[UIAlertView alloc]initWithTitle:@"注意" message:@"娃娃未收到訊息，請確認手機是否連上娃娃Wifi(MDAP XXXX)" delegate:nil cancelButtonTitle:@"確認" otherButtonTitles:nil]show];

        } else if (retVal==-2) {
            printf("\n- ERROR SENT FAIL");//失敗
            [[[UIAlertView alloc]initWithTitle:@"注意" message:@"娃娃未收到訊息，請再按一次下一步" delegate:nil cancelButtonTitle:@"確認" otherButtonTitles:nil]show];
        } else {
            //TODO:other cases
        }
    } else {
        //[API]
        printf("\n- sent_msg(%s)", sent_msg);
        printf("\n- received_msg(%s)", received_msg);
        //顯示loading
        [howToUseMDVC.registToyLoading show];
        //創建timer
        if (howToUseMDVC.registTimerOut == nil) {
            NSLog(@"計時器已經啟用");
            howToUseMDVC.registTimerOut = [NSTimer scheduledTimerWithTimeInterval:120 target:howToUseMDVC selector:@selector(timerOut) userInfo:nil repeats:YES];
        }
    }
}

-(NSString *)getLocalLanAddress:(NSString*)ipAddress
{
    NSString *address = @"";
    
    // Convert exacted ip address to family. Ex:192.168.43.22->192.168.43.0
    if(![ipAddress isEqualToString:@""]) {
        
        NSArray *ipExplode = [ipAddress componentsSeparatedByString:@"."];
        
        address = [NSString stringWithFormat:@"%@,%@,%@,%@", ipExplode[0], ipExplode[1], ipExplode[2], @"0"];
        
        //           NSArray *ipv6Explode = [ipAddress componentsSeparatedByString:@":"];
        //
        //            for (NSString *value in ipv6Explode) {
        //                NSScanner*scan=[NSScanner scannerWithString:value];
        //                unsigned int a;
        //                [scan scanHexInt:&a];
        //                NSLog(@"%u",a);
        
        //            }
        
    }
    
    //ToDo : IPV6
    
    return address;
}
-(NSString *)getIPAddress
{
    NSString *address = @"";
    struct ifaddrs *interfaces = NULL;
    /*
     ifa_next指向鏈表的下一個成員；
     ifa_name是接口名稱，以0結尾的字符串，比如eth0，lo；
     ifa_flags是接口的標識位（比如當IFF_BROADCAST或IFF_POINTOPOINT設置到此標識位時，影響聯合體變量ifu_broadaddr存儲廣播地址或ifu_dstaddr記錄點對點地址）；
     ifa_netmask存儲該接口的子網掩碼；結構體變量存儲廣播地址或點對點地址（見括弧介紹ifa_flags）；
     ifa_data存儲了該接口協議族的特殊信息，它通常是NULL（一般不關注他）。
     函數getifaddrs（int getifaddrs (struct ifaddrs **__ifap)）獲取本地網絡接口信息，將之存儲於鏈表中，鏈表頭結點指針存儲於__ifap中帶回，函數執行成功返回0，失敗返回-1，且為errno賦值。
     很顯然，函數getifaddrs用於獲取本機接口信息，比如最典型的獲取本機IP地址。
     */
    struct ifaddrs *temp_addr = NULL;
    int success = 0;
    
    // retrieve the current interfaces - returns 0 on success
    success = getifaddrs(&interfaces);
    if (success == 0)
    {
        // Loop through linked list of interfaces
        temp_addr = interfaces;
        while(temp_addr != NULL)
        {
            //  -> 結構指標運算子
            if(temp_addr->ifa_addr->sa_family == AF_INET)
            {
                // Get NSString from C String
                address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
            }
            temp_addr = temp_addr->ifa_next;
        }
    }
    
    // Free memory
    freeifaddrs(interfaces);
    
    return address;
}
-(bool) sendUDPSocket:(NSString*) msg ipAddress:(NSString*) ip port:(int) p
{
    int sock;
    struct sockaddr_in destination;//sockt ip 解構體     目標
    /*
     sin_family
     位址家族 (必須是 AF_INET)。
     sin_port
     IP 通訊埠。
     sin_addr
     IP 位址。
     sin_zero
     用來讓結構和 SOCKADDR 的大小相同的填補。
     */
    
    unsigned long int echolen;
    int broadcast = 1;
    // if that doesn't work, try this
    //char broadcast = '1';
    
    //如果訊息和ip均為nill 則返回
    if (msg == nil || ip == nil)
    {
        printf("Message and/or ip address is null\n");
        return false;
    }
    
    /* Create the UDP socket */
    /*
     在寫網路程式的時候，建立TCP socket一般是這樣的：
     sock = socket(PF_INET, SOCK_STREAM, 0);
     
     然後在綁定本地地址或連接遠程地址時需要初始化sockaddr_in結構，其中指定address family時一般設置為AF_INET，即使用IP。
     AF = Address Family
     PF = Protocol Family
     AF_INET = PF_INET
     
     所以，理論上建立socket時是指定協議，應該用PF_xxxx，設置地址時應該用AF_xxxx。當然AF_INET和 PF_INET的值是相同的，混用也不會有太大的問題。
     */
    /*
     sock_stream   是有保障的（即能保證數據正確傳送到對方）面向連接的SOCKET，多用於資料（如文件）傳送。
     sock_dgram   是無保障的面向消息的socket　，　主要用於在網絡上發廣播信息。
     SOCK_STREAM是基於TCP的，數據傳輸比較有保障。SOCK_DGRAM是基於UDP的，專門用於局域網，基於廣播。
     SOCK_STREAM 是數據流,一般是tcp/ip協議的編程,SOCK_DGRAM分是數據抱,是udp協議網絡編程
     */
    /*
     想想會有哪種協議類型是IPPROTO_IP =0的呢？內核使用IPPROTO_IP僅僅是為了在某些情況下默認匹配協議類型而已。例如SOCK_DGRAM協議族默認的協議是IPPROTO_UDP，SOCK_STREAM默認的是IPPROTO_TCP。即
     socket(PF_INET,SOCK_DGRAM,IPPROTO_IP) 和
     socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP)是一樣的，
     
     socket(PF_INET,SOCK_STREAM,IPPROTO_IP) 和
     socket(PF_INET,SOCK_DGRAM,IPPROTO_TCP)一樣。
     但是
     socket(PF_INET,SOCK_RAW,IPPROTO_IP)讓SOCK_RAW如何選擇默認的協議類型呢？
     
     實際上編程的時候大家都會指定協議類型，而不會使用IPPROTO_IP進行默認匹配。
     具體請參見inetsw的初始化過程就可以明白了。
     */
    /*
     socket 函數原型為：
     　　int socket(int domain, int type, int protocol);
     domain指明所使用的協議族，通常為PF_INET，表示互聯網協議族（TCP/IP協議族）；type參數指定socket的類型：SOCK_STREAM 或SOCK_DGRAM，Socket介面還定義了原始Socket（SOCK_RAW），允許程式使用低層協議；
     protocol通常賦值"0"。
     Socket()調用返回一個整型socket描述符，你可以在面的調用使用它。
     Socket描述符是一個指向內部數據結構的指針，它指向描述符表入口。調用Socket函數時，socket執行體將建立一個Socket，實際上"建立一個Socket"意味著為一個Socket數據結構分配存儲空間。Socket執行體為你管理描述符表。
     兩個網絡程式之間的一個網絡連接包括五種資訊：通信協議、本地協議位址、本地主機埠、遠端主機位址和遠端協議埠。Socket數據結構中包含這五種資訊。
     */
    
    //PF_INET 固定 SOCK_DGRAM是基於UDP的，專門用於局域網，基於廣播 ，IPPROTO_UDP protocol
    //建立socket
    sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP);
    
    //如果小於0 則表示失敗
    if (sock < 0)
    {
        printf("Failed to create socket\n");
        
        return false;
    }
    
    /*建立 伺服器 連線網址的結構體*/
    /*
     sin_family
     位址家族 (必須是 AF_INET)。
     sin_port
     IP 通訊埠。
     sin_addr
     IP 位址。
     sin_zero
     用來讓結構和 SOCKADDR 的大小相同的填補。
     */
    
    /* Construct the server sockaddr_in structure */
    //sizeof 運算子用來計算資料所佔用的位元組數。
    // memset() 將某一記憶體區段的前 n 個字元全部設定為某一字元。
    //設定 sockaddr_in
    memset(&destination, 0, sizeof(destination));
    
    /* Clear struct */
    destination.sin_family = AF_INET;//表示協議 一般是用 AF_INET 來代表TCP/IP協議
    
    /* Internet/IP */
    /*
     inet_addr函數需要一個字符串作為其參數，該字符串指定了以點分十進制格式表示的IP地址（例如：192.168.0.16）。而且inet_addr函數會返回一個適合分配給S_addr的u_long類型的數值。
     Inet_ntoa函数会完成相反的转换，它接受一个in_addr结构体类型的参数并返回一个以点分十进制格式表示的IP地址字符串。
     */
    destination.sin_addr.s_addr = inet_addr([ip UTF8String]);
    
    /* IP address */
    destination.sin_port = htons(p);
    
    
    
    /* server port */
    /*
     int setsockopt(
     SOCKET s,
     int level,
     int optname,
     const char* optval,
     int optlen
     );
     s(套接字): 指向一個打開的套接口描述字  1是打開 0 是關閉
     level:(級別)： 指定選項代碼的類型。
     SOL_SOCKET: 基本套接口
     IPPROTO_IP: IPv4套接口
     IPPROTO_IPV6: IPv6套接口
     IPPROTO_TCP: TCP套接口
     optname(選項名)： 選項名稱
     optval(選項值): 是一個指向變量的指針 類型：整形，套接口結構， 其他結構類型:linger{}, timeval{ }
     optlen(選項長度) ：optval 的大小
     
     返回值：標誌打開或關閉某個特徵的二進制選項
     
     以下IPV4選項用於組播
     IPv4 選項 數據類型 描 述
     IP_ADD_MEMBERSHIP struct ip_mreq 加入到組播組中
     IP_ROP_MEMBERSHIP struct ip_mreq 從組播組中退出
     IP_MULTICAST_IF struct ip_mreq 指定提交組播報文的接口
     IP_MULTICAST_TTL u_char 指定提交組播報文的TTL
     IP_MULTICAST_LOOP u_char 使組播報文環路有效或無效
     */
    
    
    
    //sock 的設定
    setsockopt(sock,
               IPPROTO_IP,
               IP_MULTICAST_IF,
               &destination,
               sizeof(destination)
               );
    //strlen() 回傳字串的長度
    const char *cmsg = [msg UTF8String];   echolen = strlen(cmsg);
    
    //TO DO:SET TIMEOUT
    struct timeval timeout;
    timeout.tv_sec = 10;
    timeout.tv_usec = 0;
    
    if (setsockopt (sock, SOL_SOCKET, SO_RCVTIMEO, (char *)&timeout,
                    sizeof(timeout)) < 0)
    {
        NSLog(@"發送超時");
        
    }
    
    if (setsockopt (sock, SOL_SOCKET, SO_SNDTIMEO, (char *)&timeout,
                    sizeof(timeout)) < 0){
        NSLog(@"接收超時");
    }
    
    
    // this call is what allows broadcast packets to be sent:
    /*
     SO_BROADCAST 允許發送廣播數據 int
     適用於 UDP socket。其意義是允許 UDP socket 「廣播」（broadcast）訊息到網路上。
     
     */
    if (setsockopt(sock,
                   SOL_SOCKET,
                   SO_BROADCAST,
                   &broadcast,
                   sizeof broadcast) == -1)
    {
        perror("setsockopt (SO_BROADCAST)");
        handledFromUDPResponse(-1, (char *)cmsg, (char *)"");
        return false;
    }
    
    /*
     sendto（經socket傳送數據）
     相關函數
     send , sendmsg,recv , recvfrom , socket
     表頭文件
     #include < sys/types.h >
     #include < sys/socket.h >
     定義函數
     int sendto ( int s , const void * msg, int len, unsigned int flags, const
     struct sockaddr * to , int tolen ) ;
     函數說明
     sendto() 用來將數據由指定的socket傳給對方主機。
     參數s為已建好連線的socket,如果利用UDP協議則不需經過連線操作。
     參數msg指向欲連線的數據內容，int len 字串的長度
     參數flags 一般設0，詳細描述請參考send()。
     參數to用來指定欲傳送的網絡地址，結構sockaddr請參考bind()。
     參數tolen為sockaddr的結果長度。
     返回值
     成功則返回實際傳送出去的字符數，失敗返回－1，錯誤原因存於errno 中。
     錯誤代碼
     EBADF 參數s非法的socket處理代碼。
     EFAULT 參數中有一指針指向無法存取的內存空間。
     WNOTSOCK canshu s為一文件描述詞，非socket。
     EINTR 被信號所中斷。
     EAGAIN 此動作會令進程阻斷，但參數s的soket為補課阻斷的。
     ENOBUFS 系統的緩衝內存不足。
     EINVAL 傳給系統調用的參數不正確。
     */
    if (sendto(sock,
               cmsg,
               echolen,
               0,
               (struct sockaddr *) &destination,
               sizeof(destination)) != echolen)
    {
        printf("Mismatch in number of sent bytes\n");//不匹配發送的字節數
        handledFromUDPResponse(-2, (char *)cmsg, (char *)"");
        return false;
    }
    else
    {
        NSLog(@"sendUDPSocket %@", msg);
        
        //receive server response test {
        /*
         ssize_t	recvfrom(int, void *, size_t, int, struct sockaddr * __restrict,
         socklen_t * __restrict) __DARWIN_ALIAS_C(recvfrom);
         ssize_t	sendto(int, const void *, size_t, int, const struct sockaddr *, socklen_t) __DARWIN_ALIAS_C(sendto);
         */
        ssize_t result = 0;//而ssize_t這個數據類型用來表示可以被執行讀寫操作的數據塊的大小.它和size_t類似,但必需是signed
        size_t bufSize = 1024;//該類型保證能容納實現所建立的最大對象的字節大小「適於計量內存中可容納的數據項目個數的無符號整數類型」
        socklen_t destinationlen = sizeof(destination);//數據類型"socklen_t"和int應該具有相同的長度.否則就會破壞 BSD套接字層的填充.POSIX開始的時候用的是size_t, Linus Torvalds(他希望有更多的人,但顯然不是很多) 努力向他們解釋使用size_t是完全錯誤的,因為在64位結構中 size_t和int的長度是不一樣的,而這個參數(也就是accept函數 的第三參數)的長度必須和int一致,因為這是BSD套接字接口 標準.最終POSIX的那幫傢伙找到瞭解決的辦法,那就是創造了 一個新的類型"socklen_t".Linux Torvalds說這是由於他們 發現了自己的錯誤但又不好意思向大傢伙兒承認,所以另外 創造了一個新的數據類型
        char *buf = malloc(bufSize);//malloc() 建立新記憶體空間
        
        result = recvfrom(sock, buf, bufSize, 0, (struct sockaddr *) &destination, &destinationlen);
        /*
         recvfrom（經socket接收數據）
         
         相關函數 recv，recvmsg，send，sendto，socket
         表頭文件   #include<sys/types.h>
         #include<sys/socket.h>
         定義函數
         int recvfrom(int s,void *buf,int len,unsigned int flags ,struct sockaddr *from ,int *fromlen);
         函數說明
         recv()用來接收遠程主機經指定的socket 傳來的數據，並把數據存到由參數buf 指向的內存空間，
         參數len 為可接收數據的最大長度。
         參數flags 一般設0，其他數值定義請參考recv()。
         參數from用來指定欲傳送的網絡地址，結構sockaddr 請參考bind()。
         參數fromlen為sockaddr的結構長度。
         返回值
         成功則返回接收到的字符數，失敗則返回-1，錯誤原因存於errno中。
         錯誤代碼
         EBADF 參數s非合法的socket處理代碼
         EFAULT 參數中有一指針指向無法存取的內存空間。
         ENOTSOCK 參數s為一文件描述詞，非socket。
         EINTR 被信號所中斷。
         EAGAIN 此動作會令進程阻斷，但參數s的socket為不可阻斷。
         ENOBUFS 系統的緩衝內存不足
         ENOMEM 核心內存不足
         EINVAL 傳給系統調用的參數不正確。
         
         */
        
        
        if(result>0) buf[result]='\0';//'\0'在c语言的字符串中表示字符串的结尾
        //NSLog(@"===recvfrom %i %@", (int)result, [NSString stringWithUTF8String:(char *)buf]);
        
        char *serverIp = inet_ntoa(destination.sin_addr);//ip轉換
        
        NSLog(@"===recvfrom %i %@", (int)result, [NSString stringWithUTF8String:(char *)serverIp]);
        //帶入結果 轉成Ｃ的文字內容 儲存結果內容的記憶體空間
        handledFromUDPResponse(result, (char *)cmsg, (char *)buf);
        if(buf) free(buf);
        //receive server response test }
        
        return true;
    }
    
    
    /*
     ssize_t是什麼類型的？
     解釋一：為了增強程序的可移植性，便有了size_t，它是為了方便系統之間的移植而定義的，不同的系統上，定義size_t可能不一樣。
     在32位系統上 定義為 unsigned int 也就是說在32位系統上是32位無符號整形。在64位系統上定義為 unsigned long 也就是說在64位系統上是64位無符號整形。size_t一般用來表示一種計數，比如有多少東西被拷貝等。例如：sizeof操作符的結果類型是size_t，該類型保證能容納實現所建立的最大對象的字節大小。 它的意義大致是「適於計量內存中可容納的數據項目個數的無符號整數類型」。所以，它在數組下標和內存管理函數之類的地方廣泛使用。而ssize_t這個數據類型用來表示可以被執行讀寫操作的數據塊的大小.它和size_t類似,但必需是signed.意即：它表示的是signed size_t類型的。
     */
}
//UDP }
//}



@end
